package com.wutong.erb.controller.auth;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import com.wutong.erb.dto.UserDTO;
import com.wutong.erb.entity.user.User;
import com.wutong.erb.filter.RateLimitFilter;
import com.wutong.erb.mapper.UserMapper;
import com.wutong.erb.other.Response;
import com.wutong.erb.service.AuthService;
import com.wutong.erb.util.CookieUtil;
import com.wutong.erb.util.DeviceUtil;
import com.wutong.erb.util.JwtUtil;
import io.github.bucket4j.Bucket;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Pattern;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseCookie;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.time.Duration;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 认证授权控制器，处理用户注册、登录、验证码等相关接口
 * @author wutong
 */
@Slf4j
@Getter
@RestController
@RequestMapping("/api/auth")
public class AuthController {
    private final AuthService authService;
    private final RedisTemplate<String, String> redisTemplate;
    private final UserMapper userMapper;
    private final Bucket rateLimiter;

    public AuthController(AuthService authService, RedisTemplate<String, String> redisTemplate, UserMapper userMapper, RateLimitFilter rateLimitFilter) {
        this.authService = authService;
        this.redisTemplate = redisTemplate;
        this.userMapper = userMapper;
        this.rateLimiter = rateLimitFilter.getBucket();
    }

    /**
     * 生成图形验证码，并返回图片流<p>
     * TODO 验证码对风险IP应该需要不同的处理
     * @param request HTTP请求
     * @param response HTTP响应
     * @throws IOException IO异常
     */
    @GetMapping("/captcha.png")
    public void getCaptcha(HttpServletRequest request, HttpServletResponse response) throws IOException {
        LineCaptcha captcha = CaptchaUtil.createLineCaptcha(130, 48, 4, 150);
        String deviceHash = DeviceUtil.generateDeviceHash(request);

        redisTemplate.opsForValue().set(
                "CAPTCHA:" + deviceHash,
                captcha.getCode(),
                5, TimeUnit.MINUTES
        );
        response.setContentType("image/png");

        captcha.write(response.getOutputStream());
    }

    @PostMapping("/email/send-code")
    public ResponseEntity<?> sendEmailCode(
            @Valid @Email @RequestParam("email") String email) {

        if (redisTemplate.hasKey("EMAIL_CAPTCHA:" + email)) {
            return Response.failure("验证码早已发送").badRequest();
        }
        authService.sendEmailCaptcha(email);
        return Response.success("验证码已发送至邮箱").ok();
    }

    @PostMapping("/register")
    public ResponseEntity<?> register(
            HttpServletRequest request,
            @Valid @RequestBody UserDTO.UserRegisterDTO dto) {
        return authService.register(request, dto);
    }

    @PostMapping("/check-username")
    public ResponseEntity<?> checkUsername(
            @Valid
            @Pattern(regexp = "^(?![0-9]+$)[A-Za-z][A-Za-z0-9_]{3,19}$")
            @RequestParam("username")
            String username) {
        return Response.success()
                .message("请求成功")
                .data(userMapper.selectByUsername(username))
                .build()
                .ok();
    }

    @PostMapping("/check-email")
    public ResponseEntity<?> checkEmail(
            @Valid
            @Email
            @RequestParam("email")
            String email) {
        return Response.success()
                .message("请求成功")
                .data(userMapper.selectByEmail(email))
                .build()
                .ok();
    }

    @PostMapping("/login")
    public ResponseEntity<?> login(
            HttpServletRequest request,
            HttpServletResponse response,
            @Valid @RequestBody UserDTO.UserLoginDTO dto) {
        AuthService.LoginStatus checkLogin = authService.login(DeviceUtil.generateDeviceHash(request), dto);
        switch (checkLogin) {
            case SUCCESS:
                User user = userMapper.selectByUsername(dto.getUsername());
                if (user == null) {
                    user = userMapper.selectByEmail(dto.getEmail());
                }
                String accessToken = JwtUtil.generateToken(
                        user.getUsername(),
                        Map.of(
                                "userId", user.getId(),
                                "userType", user.getUserType()
                        ), 12, TimeUnit.HOURS
                );
                String refreshToken = JwtUtil.generateToken(
                        DeviceUtil.generateDeviceHash(request),
                        Map.of(
                                "userId", user.getId(),
                                "accessToken", accessToken,
                                "loginTime", System.currentTimeMillis()
                        ), 7, TimeUnit.DAYS
                );
                // 双验证，用于防止CSRF攻击和退出登录使用
                log.debug("将refreshToken存入redis");
                redisTemplate.opsForValue().set("refreshToken-" + DeviceUtil.generateDeviceHash(request), refreshToken, 7, TimeUnit.DAYS);
                log.debug("设置refreshToken cookie");
                response.addCookie(
                        CookieUtil.createCookie("refreshToken", refreshToken)
                        .setPath("/")
                        .setHttpOnly(true)
                        .setDomain(request.getServerName())
                        .setMaxAge(12, TimeUnit.HOURS)
                        .build());
                return Response.success()
                        .message("登录成功")
                        .build()
                        .ok();
            case CAPTCHA_REQUIRED:
                return Response.failure("未提供验证码").badRequest();
            case CAPTCHA_FAILED:
                return Response.failure("验证码错误").badRequest();
            case LOCKED:
                return Response.failure("账号已被锁定，请稍后再试").badRequest();
            case FAILED:
                return Response.failure("用户名或密码错误").badRequest();
            default:
                return Response.failure("未知错误，请联系管理员").badRequest();
        }
    }

    @PostMapping("/need-captcha")
    public ResponseEntity<?> getFailedLoginCount(HttpServletRequest request) {
        String deviceHash = DeviceUtil.generateDeviceHash(request);
        return Response.success()
                .message("请求成功")
                .additionalData(Map.of(
                        "isNeedCaptcha", authService.shouldNeedCaptcha(deviceHash)
                ))
                .build()
                .ok();
    }

    @PostMapping("/logout")
    public ResponseEntity<?> logout(HttpServletRequest request, HttpServletResponse response) {
        String deviceHash = DeviceUtil.generateDeviceHash(request);
        // 清除登录状态，删除refreshToken
        redisTemplate.delete("refreshToken-" + deviceHash);
        return Response.success()
                .message("登出成功")
                .build()
                .ok();
    }

}

